package com.joyrec.util.db.redis

import ws.very.util.lang.DateFormats

trait RedisIndexs {

}

trait CommonIndex {
  protected def index: SingleRedisTpl
  def clearAll = index.flushDB
  def typeStartWith(start: S) =
    index.keyStartWith(start)
  def allTypes = index.allKeys
}

trait SetIndex extends CommonIndex {
  def apply(typ: S) = new {
    def +(id: S): L = index.sadd(typ, id)
    def contains(id: S) = index.sismember(typ, id)
    def ++(ids: Itb[S]) = ids.map { this + _ }
    def -(id: S) = index.srem(typ, id)
  }
}

trait QueueIndex extends CommonIndex {
  val push: B

  def apply(typ: S) = new {
    def +(id: S) = if (push) index.lpush(typ, id) else index.rpush(typ, id)
    def -(id: S) = index.lrem(typ, id, 1)
    def -+(id: S) = (this - id, this + id)
    def page(p: Int = 0)(implicit pageCount: I) = index.lrangePage(typ, p)
    def count = index.llength(typ)
    def all = ((index.lrange _).tupled)(index.allRangeLong(typ))

  }
}
object QueueIndex {
  trait Left extends QueueIndex {
    val push = true
  }
  trait Right extends QueueIndex {
    val push = false
  }
}

trait SortedSetIndex extends CommonIndex {
  class SortedSet(val typ: S) {
    @deprecated("不推荐使用，明确使用-+方便替换结构")
    def +(id: S, score: D = now.sec) = index.zadd(typ, score, id)
    def -(id: S) = index.zrem(typ, id)
    def -+(id: S, score: D = now.sec) = this + (id, score)
    def page(p: Int = 0)(implicit pageCount: I) = index.zrevrangePage(typ, p)
    def pageWithScore(p: Int = 0)(implicit pageCount: I) = index.zrevrangePageWithScore(typ, p)
    def count = index.zcard(typ)
    def all = index.zrevrangeAll(typ)
    def allWithScore = index.zrevrangeAllWithScore(typ)
    def contains(id: S) = index.zrank(typ, id) != None
  }
  def apply(typ: S) = new SortedSet(typ)
}

trait FriendshipWithSortedSetDao { //向上FirendshipDao
  protected def follow: SingleRedisTpl
  protected def fan: SingleRedisTpl

  trait ship {
    protected val fromId: S
    protected val isFollower: B
    private val followIndex = new SortedSetIndex {
      val index = follow
    }
    private val fanIndex = new SortedSetIndex {
      val index = fan
    }
    private def redis(b: B) =
      if (b) if (isFollower) followIndex else fanIndex
      else if (isFollower) fanIndex else followIndex
    def +(toId: S) = {
      val time = now.sec
      redis(true)(fromId) + (toId, time)
      redis(false)(toId) + (fromId, time)
    }
    def -(toId: S) = {
      redis(true)(fromId) - toId
      redis(false)(toId) - fromId
    }
    def page(page: I)(implicit pageCount: I) = redis(true)(fromId).page(page)

    def all = redis(true)(fromId).all

    def contains(toId: S) = redis(true)(fromId).contains(toId)

    def count = redis(true)(fromId).count.toInt
  }

  protected trait followerShip extends ship {
    val isFollower = true
  }

  protected trait idolShip extends ship {
    val isFollower = false
  }
  def idol(id: S) = new idolShip {
    val fromId = id
  }
  def follower(id: S) = new followerShip {
    val fromId = id
  }

  trait idols {
    def allIds = fan.allKeys
    def allIdsSortByCount = allIds.map { id =>
      id -> idol(id).count
    }.toSeq.sortWith { case ((_, c1), (_, c2)) => c1 > c2 }

    def allIdsSortByCountPage(p: I)(implicit pageCount: I) =
      allIdsSortByCount.slice(p * pageCount, pageCount * (p + 1))
  }

}
//FIXME:分数需要是时间秒
trait SortedSetTopableIndex {
  def index: SortedSetIndex
  protected val self: this.type = this
  protected def defaultScore: D = now.sec
  trait SortedSetTopable {
    val indexType: S
    def allIndex = self.allIndex(indexType)
    def indexPage(p: I = 0)(implicit pageCount: I) = self.indexPage(indexType, p)
    def indexPageWithTopN(p: I = 0)(implicit pageCount: I) = self.indexPageWithTopN(indexType, p)
    def clearIndex = self.clearIndex(indexType, _: S)
    def setBasicIfNotExists(id: S, score: D = defaultScore) = self.setBasicIfNotExists(indexType, id, score)
    def setBasic(id: S, score: D = defaultScore) = self.setBasic(indexType, id, score)
    def setTop(id: S, no: D = 0) = self.setTop(indexType, id, no)
    def contains(id:S) = self.contains(indexType,id)
  }
  def apply(typ: S) = new SortedSetTopable { val indexType = typ }

  protected val KeyRegex = """(.+)`(\d)""".r

  protected def key2Index(key: S) =
    key match {
      case KeyRegex(id, isTop) =>
        id -> (isTop == "1")
    }
  protected def index2Key(id: S, isTop: B) =
    s"$id`${if (isTop) 1 else 0}"

  protected val topLimit = DateFormats.`yyyy-MM-dd HH:mm:ss`.parse("3000-01-01 00:00:00").getTime() / 1000

  def allIndex(typ: S) = toWithTopN(index(typ).allWithScore)

  private def toWithTopN(keyAndScores: Seq[(String, Double)]) = keyAndScores.map {
    case (k, v) => key2Index(k) match {
      case (id, isTop) => if (isTop)
        id -> (Some(v - topLimit))
      else id -> None
    }
  }
  def indexPage(typ: S, p: I = 0)(implicit pageCount: I) =
    index(typ).page(p)(pageCount).map { key2Index }

  def indexPageWithTopN(typ: S, p: I = 0)(implicit pageCount: I) = toWithTopN(index(typ).pageWithScore(p)(pageCount))

  protected def clear(typ: S, id: S, isTop: B) =
    index(typ) - index2Key(id, isTop)
  def clearIndex(typ: S, id: S) = {
    clear(typ, id, true)
    clear(typ, id, false)
  }
  def setBasicIfNotExists(typ: S, id: S, score: D = defaultScore) = {
    if ((!index(typ).contains(index2Key(id, false)) && !index(typ).contains(index2Key(id, true))))
      index(typ) + (index2Key(id, false), score)
  }

  def setBasic(typ: S, id: S, score: D = defaultScore) = {
    clear(typ, id, true)
    index(typ) + (index2Key(id, false), score)
  }
  def setTop(typ: S, id: S, no: D = 0) = {
    clearIndex(typ, id)
    index(typ) + (index2Key(id, true), topLimit + no)
  }

  def allIndexTypes = index.allTypes

  def contains(typ:S,id:S) =index(typ).contains(index2Key(id, false)) || index(typ).contains(index2Key(id, true))

}
object SortedSetTopableIndex {
  trait WithAllType extends SortedSetTopableIndex {
    val allIndexTypeName = "all"
    def allIndexType = this(allIndexTypeName)

    def clearAllTypeIndex(oldTyp: S, id: S) = {
      clearIndex(oldTyp, id)
      clearIndex(allIndexTypeName, id)
    }

    def newIndex(typ: S, id: S, score: D = defaultScore) = {
      setBasic(typ, id, score)
      setBasic(allIndexTypeName, id, score)
    }
    trait SortedSetTopable extends super.SortedSetTopable {
      def clearAllTypeIndex(id: S) = self.clearAllTypeIndex(indexType, id)
      def newIndex(id: S, score: D = defaultScore) = self.newIndex(indexType, id, score)
    }
    override def apply(typ: S) = new SortedSetTopable { val indexType = typ }
  }
}

trait PeriodIndex extends SingleRedisTpl {
  import scala.concurrent.duration._
  protected val beginKey = "b"
  protected val endKey = "e"
  protected implicit def toScore(dur: Duration) = dur.toSeconds.toDouble

  protected def defaultAhead = 0.milli
  protected def defaultDelay = 0.milli

  def update(id: S, period: ((Duration, Duration))) = {
    val (begin, end) = period
    zadd(beginKey, begin, id)
    zadd(endKey, end, id)
  }
  def inPeriod(base: Duration = now.mill.milli, ahead: Duration = defaultAhead, delay: Duration = defaultDelay) =
    zrangeByScore(endKey, toScore(base - delay).toString, "+inf") & zrangeByScore(beginKey, "-inf", toScore(base + ahead).toString)

  def delIndex(id: S) = {
    zrem(beginKey, id)
    zrem(endKey, id)
  }

}
